/*******************************************************************************
 * Copyright (c) 2000, 2011 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ceylon.ide.eclipse.code.explorer;

import static org.eclipse.ceylon.ide.eclipse.ui.CeylonPlugin.PLUGIN_ID;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.PerformanceStats;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.help.IContextProvider;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJarEntryResource;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.dnd.JdtViewerDragSupport;
import org.eclipse.jdt.internal.ui.dnd.JdtViewerDropSupport;
import org.eclipse.jdt.internal.ui.filters.OutputFolderFilter;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.internal.ui.packageview.ClassPathContainer;
import org.eclipse.jdt.internal.ui.packageview.PackagesMessages;
import org.eclipse.jdt.internal.ui.packageview.WorkingSetAwareJavaElementSorter;
import org.eclipse.jdt.internal.ui.preferences.MembersOrderPreferenceCache;
import org.eclipse.jdt.internal.ui.util.JavaUIHelp;
import org.eclipse.jdt.internal.ui.util.SelectionUtil;
import org.eclipse.jdt.internal.ui.viewsupport.AppearanceAwareLabelProvider;
import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
import org.eclipse.jdt.internal.ui.viewsupport.FilterUpdater;
import org.eclipse.jdt.internal.ui.viewsupport.IRefreshable;
import org.eclipse.jdt.internal.ui.viewsupport.IViewPartInputProvider;
import org.eclipse.jdt.internal.ui.viewsupport.ProblemTreeViewer;
import org.eclipse.jdt.internal.ui.viewsupport.StatusBarUpdater;
import org.eclipse.jdt.internal.ui.workingsets.ConfigureWorkingSetAction;
import org.eclipse.jdt.internal.ui.workingsets.WorkingSetFilterActionGroup;
import org.eclipse.jdt.internal.ui.workingsets.WorkingSetModel;
import org.eclipse.jdt.ui.IPackagesViewPart;
import org.eclipse.jdt.ui.JavaElementComparator;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.StandardJavaElementContentProvider;
import org.eclipse.jdt.ui.actions.CustomFiltersActionGroup;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IElementComparer;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.OpenAndLinkWithEditorHelper;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.actions.ActionContext;
import org.eclipse.ui.part.ISetSelectionTarget;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.IShowInTarget;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.views.framelist.Frame;
import org.eclipse.ui.views.framelist.FrameAction;
import org.eclipse.ui.views.framelist.FrameList;
import org.eclipse.ui.views.framelist.IFrameSource;
import org.eclipse.ui.views.framelist.TreeFrame;

/**
 * The ViewPart for the Package Explorer. It listens to part activation events. When selection
 * linking with the editor is enabled the view selection tracks the active editor page. Similarly
 * when a resource is selected in the packages view the corresponding editor is activated.
 */
public class PackageExplorerPart extends ViewPart
    implements ISetSelectionTarget, IMenuListener,
        IShowInTarget, IRefreshable,
        IPackagesViewPart,  IPropertyChangeListener,
        IViewPartInputProvider {

    private static final String PERF_CREATE_PART_CONTROL= "org.eclipse.jdt.ui/perf/explorer/createPartControl"; //$NON-NLS-1$
    private static final String PERF_MAKE_ACTIONS= "org.eclipse.jdt.ui/perf/explorer/makeActions"; //$NON-NLS-1$

    private static final int HIERARCHICAL_LAYOUT= 0x1;
    private static final int FLAT_LAYOUT= 0x2;

    public static final int PROJECTS_AS_ROOTS= 1;
    public static final int WORKING_SETS_AS_ROOTS= 2;

    public final static String VIEW_ID= PLUGIN_ID + ".view.PackageExplorer";

    // Persistence tags.
    private static final String TAG_LAYOUT= "layout"; //$NON-NLS-1$
    private static final String TAG_GROUP_LIBRARIES= "group_libraries"; //$NON-NLS-1$
    private static final String TAG_ROOT_MODE= "rootMode"; //$NON-NLS-1$
    private static final String TAG_LINK_EDITOR= "linkWithEditor"; //$NON-NLS-1$
    private static final String TAG_MEMENTO= "memento"; //$NON-NLS-1$

    private boolean fIsCurrentLayoutFlat; // true means flat, false means hierarchical
    private boolean fShowLibrariesNode;
    private boolean fLinkingEnabled;

    private int fRootMode;
    private WorkingSetModel fWorkingSetModel;

    private PackageExplorerLabelProvider fLabelProvider;
    private DecoratingJavaLabelProvider fDecoratingLabelProvider;
    private PackageExplorerContentProvider fContentProvider;
    private FilterUpdater fFilterUpdater;

    private PackageExplorerActionGroup fActionSet;
    private ProblemTreeViewer fViewer;
    private Menu fContextMenu;

    private IMemento fMemento;

    /**
     * Helper to open and activate editors.
     * @since 3.5
     */
    private OpenAndLinkWithEditorHelper fOpenAndLinkWithEditorHelper;

    private String fWorkingSetLabel;
    private final IDialogSettings fDialogSettings;


    private final IPartListener2 fLinkWithEditorListener= new IPartListener2() {
        public void partVisible(IWorkbenchPartReference partRef) {}
        public void partBroughtToTop(IWorkbenchPartReference partRef) {}
        public void partClosed(IWorkbenchPartReference partRef) {}
        public void partDeactivated(IWorkbenchPartReference partRef) {}
        public void partHidden(IWorkbenchPartReference partRef) {}
        public void partOpened(IWorkbenchPartReference partRef) {}
        public void partInputChanged(IWorkbenchPartReference partRef) {
            IWorkbenchPage activePage= JavaPlugin.getActivePage();
            if (partRef instanceof IEditorReference && activePage != null && activePage.getActivePartReference() == partRef) {
                editorActivated(((IEditorReference) partRef).getEditor(true));
            }
        }

        public void partActivated(IWorkbenchPartReference partRef) {
            if (partRef instanceof IEditorReference) {
                editorActivated(((IEditorReference) partRef).getEditor(true));
            }
        }

    };

    private final ITreeViewerListener fExpansionListener= new ITreeViewerListener() {
        public void treeCollapsed(TreeExpansionEvent event) {
        }

        public void treeExpanded(TreeExpansionEvent event) {
            Object element= event.getElement();
            if (element instanceof ICompilationUnit ||
                element instanceof IClassFile)
                expandMainType(element);
        }
    };


    private class PackageExplorerProblemTreeViewer extends ProblemTreeViewer {
        // fix for 64372  Projects showing up in Package Explorer twice [package explorer]
        private final List<Object> fPendingRefreshes;

        public PackageExplorerProblemTreeViewer(Composite parent, int style) {
            super(parent, style);
            fPendingRefreshes= Collections.synchronizedList(new ArrayList<Object>());
        }
        @Override
        public void add(Object parentElement, Object[] childElements) {
            if (fPendingRefreshes.contains(parentElement)) {
                return;
            }
            super.add(parentElement, childElements);
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.viewers.AbstractTreeViewer#internalRefresh(java.lang.Object, boolean)
         */
        @Override
        protected void internalRefresh(Object element, boolean updateLabels) {
            try {
                fPendingRefreshes.add(element);
                super.internalRefresh(element, updateLabels);
            } finally {
                fPendingRefreshes.remove(element);
            }
        }

        @Override
        protected boolean evaluateExpandableWithFilters(Object parent) {
            if (parent instanceof IJavaProject
                    || parent instanceof ICompilationUnit || parent instanceof IClassFile
                    || parent instanceof ClassPathContainer) {
                return false;
            }
            if (parent instanceof IPackageFragmentRoot && ((IPackageFragmentRoot) parent).isArchive()) {
                return false;
            }
            return true;
        }

        @Override
        protected boolean isFiltered(Object object, Object parent, ViewerFilter[] filters) {
            boolean res= super.isFiltered(object, parent, filters);
            if (res && isEssential(object)) {
                return false;
            }
            return res;
        }

        /* Checks if a filtered object in essential (i.e. is a parent that
         * should not be removed).
         */
        private boolean isEssential(Object object) {
            try {
                if (!isFlatLayout() && object instanceof IPackageFragment) {
                    IPackageFragment fragment = (IPackageFragment) object;
                    if (!fragment.isDefaultPackage() && fragment.hasSubpackages()) {
                        return hasFilteredChildren(fragment);
                    }
                }
            } catch (JavaModelException e) {
                JavaPlugin.log(e);
            }
            return false;
        }
        
        @Override
        protected void handleInvalidSelection(ISelection invalidSelection, ISelection newSelection) {
            IStructuredSelection is= (IStructuredSelection)invalidSelection;
            @SuppressWarnings("unchecked")
            List<Object> ns= newSelection instanceof IStructuredSelection ?
                new ArrayList<Object>(((IStructuredSelection)newSelection).toList()) :
                new ArrayList<Object>();
            boolean changed= false;
            for (Iterator<?> iter= is.iterator(); iter.hasNext();) {
                Object element= iter.next();
                if (element instanceof IJavaProject) {
                    IProject project= ((IJavaProject)element).getProject();
                    if (!project.isOpen() && project.exists()) {
                        ns.add(project);
                        changed= true;
                    }
                } else if (element instanceof IProject) {
                    IProject project= (IProject)element;
                    if (project.isOpen()) {
                        IJavaProject jProject= JavaCore.create(project);
                        if (jProject != null && jProject.exists())
                            ns.add(jProject);
                            changed= true;
                    }
                }
            }
            if (changed) {
                newSelection= new StructuredSelection(ns);
                setSelection(newSelection);
            }
            super.handleInvalidSelection(invalidSelection, newSelection);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected Object[] addAditionalProblemParents(Object[] elements) {
            if (getRootMode() == WORKING_SETS_AS_ROOTS && elements != null) {
                return fWorkingSetModel.addWorkingSets(elements);
            }
            return elements;
        }

    }

    public PackageExplorerPart() {

        // exception: initialize from preference
        fDialogSettings= JavaPlugin.getDefault().getDialogSettingsSection(getClass().getName());

        // on by default
        fShowLibrariesNode= fDialogSettings.get(TAG_GROUP_LIBRARIES) == null || fDialogSettings.getBoolean(TAG_GROUP_LIBRARIES);

        fLinkingEnabled= fDialogSettings.getBoolean(TAG_LINK_EDITOR);

        try {
            fIsCurrentLayoutFlat= fDialogSettings.getInt(TAG_LAYOUT) == FLAT_LAYOUT;
        } catch (NumberFormatException e) {
            fIsCurrentLayoutFlat= true;
        }

        try {
            fRootMode= fDialogSettings.getInt(TAG_ROOT_MODE);
        } catch (NumberFormatException e) {
            fRootMode= PROJECTS_AS_ROOTS;
        }

    }

    @Override
    public void init(IViewSite site, IMemento memento) throws PartInitException {
        super.init(site, memento);
        if (memento == null) {
            String persistedMemento= fDialogSettings.get(TAG_MEMENTO);
            if (persistedMemento != null) {
                try {
                    memento= XMLMemento.createReadRoot(new StringReader(persistedMemento));
                } catch (WorkbenchException e) {
                    // don't do anything. Simply don't restore the settings
                }
            }
        }
        fMemento= memento;
        if (memento != null) {
            restoreLayoutState(memento);
            restoreLinkingEnabled(memento);
            restoreRootMode(memento);
        }
        if (getRootMode() == WORKING_SETS_AS_ROOTS) {
            createWorkingSetModel();
        }
    }

    private void restoreRootMode(IMemento memento) {
        Integer value= memento.getInteger(TAG_ROOT_MODE);
        fRootMode= value == null ? PROJECTS_AS_ROOTS : value.intValue();
        if (fRootMode != PROJECTS_AS_ROOTS && fRootMode != WORKING_SETS_AS_ROOTS)
            fRootMode= PROJECTS_AS_ROOTS;
    }

    private void restoreLayoutState(IMemento memento) {
        Integer layoutState= memento.getInteger(TAG_LAYOUT);
        fIsCurrentLayoutFlat= layoutState == null || layoutState.intValue() == FLAT_LAYOUT;

        // on by default
        Integer groupLibraries= memento.getInteger(TAG_GROUP_LIBRARIES);
        fShowLibrariesNode= groupLibraries == null || groupLibraries.intValue() != 0;
    }

    /**
     * Returns the package explorer part of the active perspective. If
     * there isn't any package explorer part <code>null</code> is returned.
     * @return the package explorer from the active perspective
     */
    public static PackageExplorerPart getFromActivePerspective() {
        IWorkbenchPage activePage= JavaPlugin.getActivePage();
        if (activePage == null)
            return null;
        IViewPart view= activePage.findView(VIEW_ID);
        if (view instanceof PackageExplorerPart)
            return (PackageExplorerPart)view;
        return null;
    }

    /**
     * Makes the package explorer part visible in the active perspective. If there
     * isn't a package explorer part registered <code>null</code> is returned.
     * Otherwise the opened view part is returned.
     * @return the opened package explorer
     */
    public static PackageExplorerPart openInActivePerspective() {
        try {
            return (PackageExplorerPart)JavaPlugin.getActivePage().showView(VIEW_ID);
        } catch(PartInitException pe) {
            return null;
        }
    }

     @Override
    public void dispose() {
        XMLMemento memento= XMLMemento.createWriteRoot("packageExplorer"); //$NON-NLS-1$
        saveState(memento);
        StringWriter writer= new StringWriter();
        try {
            memento.save(writer);
            fDialogSettings.put(TAG_MEMENTO, writer.getBuffer().toString());
        } catch (IOException e) {
            // don't do anything. Simply don't store the settings
        }

        if (fContextMenu != null && !fContextMenu.isDisposed())
            fContextMenu.dispose();

        getSite().getPage().removePartListener(fLinkWithEditorListener); // always remove even if we didn't register

        JavaPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
        if (fViewer != null)
            fViewer.removeTreeListener(fExpansionListener);

        if (fActionSet != null)
            fActionSet.dispose();
        if (fFilterUpdater != null)
            ResourcesPlugin.getWorkspace().removeResourceChangeListener(fFilterUpdater);
        if (fWorkingSetModel != null)
            fWorkingSetModel.dispose();

        super.dispose();
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
     */
    @Override
    public void createPartControl(Composite parent) {

        final PerformanceStats stats= PerformanceStats.getStats(PERF_CREATE_PART_CONTROL, this);
        stats.startRun();

        fViewer= createViewer(parent);
        fViewer.setUseHashlookup(true);

        initDragAndDrop();

        setProviders();

        JavaPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this);


        MenuManager menuMgr= new MenuManager("#PopupMenu"); //$NON-NLS-1$
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(this);
        fContextMenu= menuMgr.createContextMenu(fViewer.getTree());
        fViewer.getTree().setMenu(fContextMenu);

        // Register viewer with site. This must be done before making the actions.
        IWorkbenchPartSite site= getSite();
        site.registerContextMenu(menuMgr, fViewer);
        site.setSelectionProvider(fViewer);

        makeActions(); // call before registering for selection changes

        // Set input after filter and sorter has been set. This avoids resorting and refiltering.
        restoreFilterAndSorter();
        fViewer.setInput(findInputElement());
        initFrameActions();
        initKeyListener();

        fViewer.addDoubleClickListener(new IDoubleClickListener() {
            public void doubleClick(DoubleClickEvent event) {
                fActionSet.handleDoubleClick(event);
            }
        });

        fOpenAndLinkWithEditorHelper= new OpenAndLinkWithEditorHelper(fViewer) {
            @Override
            protected void activate(ISelection selection) {
                try {
                    final Object selectedElement= SelectionUtil.getSingleElement(selection);
                    if (EditorUtility.isOpenInEditor(selectedElement) != null)
                        EditorUtility.openInEditor(selectedElement, true);
                } catch (PartInitException ex) {
                    // ignore if no editor input can be found
                }
            }

            @Override
            protected void linkToEditor(ISelection selection) {
                PackageExplorerPart.this.linkToEditor(selection);
            }

            @Override
            protected void open(ISelection selection, boolean activate) {
                fActionSet.handleOpen(selection, activate);
            }

        };

        IStatusLineManager slManager= getViewSite().getActionBars().getStatusLineManager();
        fViewer.addSelectionChangedListener(new StatusBarUpdater(slManager));
        fViewer.addTreeListener(fExpansionListener);

        // Set help for the view
        JavaUIHelp.setHelp(fViewer, IJavaHelpContextIds.PACKAGES_VIEW);

        fillActionBars();

        updateTitle();

        fFilterUpdater= new FilterUpdater(fViewer);
        ResourcesPlugin.getWorkspace().addResourceChangeListener(fFilterUpdater);

        // Sync'ing the package explorer has to be done here. It can't be done
        // when restoring the link state since the package explorers input isn't
        // set yet.
        setLinkingEnabled(isLinkingEnabled());

        stats.endRun();
    }

    private void initFrameActions() {
        fActionSet.getUpAction().update();
        fActionSet.getBackAction().update();
        fActionSet.getForwardAction().update();
    }

    private ProblemTreeViewer createViewer(Composite composite) {
        return new PackageExplorerProblemTreeViewer(composite, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
    }

    /**
     * Answers whether this part shows the packages flat or hierarchical.
     * @return <true> if flat layout is selected
     *
     * @since 2.1
     */
    public boolean isFlatLayout() {
        return fIsCurrentLayoutFlat;
    }

    private void setProviders() {
        //content provider must be set before the label provider
        fContentProvider= createContentProvider();
        fContentProvider.setIsFlatLayout(fIsCurrentLayoutFlat);
        fContentProvider.setShowLibrariesNode(fShowLibrariesNode);
        fViewer.setContentProvider(fContentProvider);

        fViewer.setComparer(createElementComparer());

        fLabelProvider= createLabelProvider();
        fLabelProvider.setIsFlatLayout(fIsCurrentLayoutFlat);
        fDecoratingLabelProvider= new DecoratingJavaLabelProvider(fLabelProvider, false, fIsCurrentLayoutFlat);
        fViewer.setLabelProvider(fDecoratingLabelProvider);
        // problem decoration provided by PackageLabelProvider
    }

    public void setShowLibrariesNode(boolean enabled) {
        fShowLibrariesNode= enabled;
        saveDialogSettings();

        fContentProvider.setShowLibrariesNode(enabled);
        fViewer.getControl().setRedraw(false);
        fViewer.refresh();
        fViewer.getControl().setRedraw(true);
    }

    boolean isLibrariesNodeShown() {
        return fShowLibrariesNode;
    }


    public void setFlatLayout(boolean enable) {
        // Update current state and inform content and label providers
        fIsCurrentLayoutFlat= enable;
        saveDialogSettings();

        if (fViewer != null) {
            fContentProvider.setIsFlatLayout(isFlatLayout());
            fLabelProvider.setIsFlatLayout(isFlatLayout());
            fDecoratingLabelProvider.setFlatPackageMode(isFlatLayout());

            fViewer.getControl().setRedraw(false);
            fViewer.refresh();
            fViewer.getControl().setRedraw(true);
        }
    }

    /**
     * This method should only be called inside this class
     * and from test cases.
     * @return the created content provider
     */
    public PackageExplorerContentProvider createContentProvider() {
        IPreferenceStore store= PreferenceConstants.getPreferenceStore();
        boolean showCUChildren= store.getBoolean(PreferenceConstants.SHOW_CU_CHILDREN);
        if (getRootMode() == PROJECTS_AS_ROOTS)
            return new PackageExplorerContentProvider(showCUChildren);
        else
            return new WorkingSetAwareContentProvider(showCUChildren, fWorkingSetModel);
    }

    private PackageExplorerLabelProvider createLabelProvider() {
        return new PackageExplorerLabelProvider(fContentProvider);
    }

    private IElementComparer createElementComparer() {
        if (getRootMode() == PROJECTS_AS_ROOTS)
            return null;
        else
            return WorkingSetModel.COMPARER;
    }

    private void fillActionBars() {
        IActionBars actionBars= getViewSite().getActionBars();
        fActionSet.fillActionBars(actionBars);
    }

    private Object findInputElement() {
        if (getRootMode() == WORKING_SETS_AS_ROOTS) {
            return fWorkingSetModel;
        } else {
            Object input= getSite().getPage().getInput();
            if (input instanceof IWorkspace) {
                return JavaCore.create(((IWorkspace)input).getRoot());
            } else if (input instanceof IContainer) {
                IJavaElement element= JavaCore.create((IContainer)input);
                if (element != null && element.exists())
                    return element;
                return input;
            }
            //1GERPRT: ITPJUI:ALL - Packages View is empty when shown in Type Hierarchy Perspective
            // we can't handle the input
            // fall back to show the workspace
            return JavaCore.create(JavaPlugin.getWorkspace().getRoot());
        }
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class)
     */
    @Override
    public Object getAdapter(@SuppressWarnings("rawtypes") Class key) {
        if (key.equals(ISelectionProvider.class))
            return fViewer;
        if (key == IShowInSource.class) {
            return getShowInSource();
        }
        if (key == IShowInTargetList.class) {
            return new IShowInTargetList() {
                public String[] getShowInTargetIds() {
                    return new String[] { JavaPlugin.ID_RES_NAV };
                }

            };
        }
        if (key == IContextProvider.class) {
            return JavaUIHelp.getHelpContextProvider(this, IJavaHelpContextIds.PACKAGES_VIEW);
        }
        return super.getAdapter(key);
    }

    /**
     * Returns the tool tip text for the given element.
     * @param element the element
     * @return the tooltip
     */
    String getToolTipText(Object element) {
        String result;
        if (!(element instanceof IResource)) {
            if (element instanceof IJavaModel) {
                result= PackagesMessages.PackageExplorerPart_workspace;
            } else if (element instanceof IJavaElement){
                result= JavaElementLabels.getTextLabel(element, JavaElementLabels.ALL_FULLY_QUALIFIED);
            } else if (element instanceof IWorkingSet) {
                result= ((IWorkingSet)element).getLabel();
            } else if (element instanceof WorkingSetModel) {
                result= PackagesMessages.PackageExplorerPart_workingSetModel;
            } else {
                result= fLabelProvider.getText(element);
            }
        } else {
            IPath path= ((IResource) element).getFullPath();
            if (path.isRoot()) {
                result= PackagesMessages.PackageExplorer_title;
            } else {
                result= BasicElementLabels.getPathLabel(path, false);
            }
        }

        if (fRootMode == PROJECTS_AS_ROOTS) {
            if (fWorkingSetLabel == null)
                return result;
            if (result.length() == 0)
                return Messages.format(PackagesMessages.PackageExplorer_toolTip, new String[] { fWorkingSetLabel });
            return Messages.format(PackagesMessages.PackageExplorer_toolTip2, new String[] { result, fWorkingSetLabel });
        } else { // Working set mode. During initialization element and action set can be null.
            if (element != null && !(element instanceof IWorkingSet) && !(element instanceof WorkingSetModel) && fActionSet != null) {
                FrameList frameList= fActionSet.getFrameList();
                int index= frameList.getCurrentIndex();
                IWorkingSet ws= null;
                while(index >= 0) {
                    Frame frame= frameList.getFrame(index);
                    if (frame instanceof TreeFrame) {
                        Object input= ((TreeFrame)frame).getInput();
                        if (input instanceof IWorkingSet) {
                            ws= (IWorkingSet) input;
                            break;
                        }
                    }
                    index--;
                }
                if (ws != null) {
                    return Messages.format(PackagesMessages.PackageExplorer_toolTip3, new String[] { BasicElementLabels.getWorkingSetLabel(ws) , result});
                } else {
                    return result;
                }
            } else {
                return result;
            }
        }
    }

    @Override
    public String getTitleToolTip() {
        if (fViewer == null)
            return super.getTitleToolTip();
        return getToolTipText(fViewer.getInput());
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
     */
    @Override
    public void setFocus() {
        fViewer.getTree().setFocus();
    }

    private ISelection getSelection() {
        return fViewer.getSelection();
    }

    //---- Action handling ----------------------------------------------------------

    /* (non-Javadoc)
     * @see IMenuListener#menuAboutToShow(IMenuManager)
     */
    public void menuAboutToShow(IMenuManager menu) {
        JavaPlugin.createStandardGroups(menu);

        fActionSet.setContext(new ActionContext(getSelection()));
        fActionSet.fillContextMenu(menu);
        fActionSet.setContext(null);
    }

    private void makeActions() {

        final PerformanceStats stats= PerformanceStats.getStats(PERF_MAKE_ACTIONS, this);
        stats.startRun();

        fActionSet= new PackageExplorerActionGroup(this);
        if (fWorkingSetModel != null)
            fActionSet.getWorkingSetActionGroup().setWorkingSetModel(fWorkingSetModel);

        stats.endRun();
    }

    // ---- Event handling ----------------------------------------------------------

    private void initDragAndDrop() {
        initDrag();
        initDrop();
    }

    private void initDrag() {
        new JdtViewerDragSupport(fViewer).start();
    }

    private void initDrop() {
        JdtViewerDropSupport dropSupport= new JdtViewerDropSupport(fViewer);
        dropSupport.addDropTargetListener(new WorkingSetDropAdapter(this));
        dropSupport.start();
    }

    /* (non-Javadoc)
     * @see org.eclipse.jdt.internal.ui.viewsupport.IRefreshable#refresh(org.eclipse.jface.viewers.IStructuredSelection)
     */
    public void refresh(IStructuredSelection selection) {
        Object[] selectedElements= selection.toArray();
        for (int i= 0; i < selectedElements.length; i++) {
            fViewer.refresh(selectedElements[i]);
        }
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.part.ISetSelectionTarget#selectReveal(org.eclipse.jface.viewers.ISelection)
     */
    public void selectReveal(final ISelection selection) {
        Control ctrl= getTreeViewer().getControl();
        if (ctrl == null || ctrl.isDisposed())
            return;

        fContentProvider.runPendingUpdates();
        fViewer.setSelection(convertSelection(selection), true);
    }

    public ISelection convertSelection(ISelection s) {
        if (!(s instanceof IStructuredSelection))
            return s;

        Object[] elements= ((IStructuredSelection)s).toArray();

        boolean changed= false;
        for (int i= 0; i < elements.length; i++) {
            Object convertedElement= convertElement(elements[i]);
            changed= changed || convertedElement != elements[i];
            elements[i]= convertedElement;
        }
        if (changed)
            return new StructuredSelection(elements);
        else
            return s;
    }

    private Object convertElement(Object original) {
        if (original instanceof IJavaElement) {
            if (original instanceof ICompilationUnit) {
                ICompilationUnit cu= (ICompilationUnit) original;
                IJavaProject javaProject= cu.getJavaProject();
                if (javaProject != null && javaProject.exists() && ! javaProject.isOnClasspath(cu)) {
                    // could be a working copy of a .java file that is not on classpath
                    IResource resource= cu.getResource();
                    if (resource != null)
                        return resource;
                }

            }
            return original;

        } else if (original instanceof IResource) {
            IJavaElement je= JavaCore.create((IResource)original);
            if (je != null && je.exists()) {
                IJavaProject javaProject= je.getJavaProject();
                if (javaProject != null && javaProject.exists()) {
                    if (javaProject.equals(je) || javaProject.isOnClasspath(je)) {
                        return je;
                    } else {
                        // a working copy of a .java file that is not on classpath
                        return original;
                    }
                }
            }
        } else if (original instanceof IAdaptable) {
            IAdaptable adaptable= (IAdaptable)original;
            IJavaElement je= (IJavaElement) adaptable.getAdapter(IJavaElement.class);
            if (je != null && je.exists())
                return je;

            IResource r= (IResource) adaptable.getAdapter(IResource.class);
            if (r != null) {
                je= JavaCore.create(r);
                if (je != null && je.exists())
                    return je;
                else
                    return r;
            }
        }
        return original;
    }

    public void selectAndReveal(Object element) {
        selectReveal(new StructuredSelection(element));
    }

    public boolean isLinkingEnabled() {
        return fLinkingEnabled;
    }

    /**
     * Links to editor (if option enabled)
     * @param selection the selection
     */
    private void linkToEditor(ISelection selection) {
        Object obj= SelectionUtil.getSingleElement(selection);
        if (obj != null) {
            IEditorPart part= EditorUtility.isOpenInEditor(obj);
            if (part != null) {
                IWorkbenchPage page= getSite().getPage();
                page.bringToTop(part);
                if (obj instanceof IJavaElement)
                    EditorUtility.revealInEditor(part, (IJavaElement)obj);
            }
        }
    }

    @Override
    public void saveState(IMemento memento) {
        if (fViewer == null && fMemento != null) {
            // part has not been created -> keep the old state
            memento.putMemento(fMemento);
            return;
        }

        memento.putInteger(TAG_ROOT_MODE, fRootMode);
        if (fWorkingSetModel != null)
            fWorkingSetModel.saveState(memento);

        saveLayoutState(memento);
        saveLinkingEnabled(memento);

        if (fActionSet != null) {
            fActionSet.saveFilterAndSorterState(memento);
        }
    }

    private void saveLinkingEnabled(IMemento memento) {
        memento.putInteger(TAG_LINK_EDITOR, fLinkingEnabled ? 1 : 0);
    }

    private void saveLayoutState(IMemento memento) {
        if (memento != null) {
            memento.putInteger(TAG_LAYOUT, getLayoutAsInt());
            memento.putInteger(TAG_GROUP_LIBRARIES, fShowLibrariesNode ? 1 : 0);
        }
    }

    private void saveDialogSettings() {
        fDialogSettings.put(TAG_GROUP_LIBRARIES, fShowLibrariesNode);
        fDialogSettings.put(TAG_LAYOUT, getLayoutAsInt());
        fDialogSettings.put(TAG_ROOT_MODE, fRootMode);
        fDialogSettings.put(TAG_LINK_EDITOR, fLinkingEnabled);
    }

    private int getLayoutAsInt() {
        if (fIsCurrentLayoutFlat)
            return FLAT_LAYOUT;
        else
            return HIERARCHICAL_LAYOUT;
    }

    private void restoreFilterAndSorter() {
        fViewer.addFilter(new OutputFolderFilter());
        setComparator();
        if (fMemento != null)
            fActionSet.restoreFilterAndSorterState(fMemento);
    }

    private void restoreLinkingEnabled(IMemento memento) {
        Integer val= memento.getInteger(TAG_LINK_EDITOR);
        fLinkingEnabled= val != null && val.intValue() != 0;
    }

    /**
     * Create the KeyListener for doing the refresh on the viewer.
     */
    private void initKeyListener() {
        fViewer.getControl().addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent event) {
                fActionSet.handleKeyEvent(event);
            }
        });
    }

    /**
     * An editor has been activated.  Set the selection in this Packages Viewer
     * to be the editor's input, if linking is enabled.
     * @param editor the activated editor
     */
    void editorActivated(IEditorPart editor) {
        IEditorInput editorInput= editor.getEditorInput();
        if (editorInput == null)
            return;
        Object input= getInputFromEditor(editorInput);
        if (input == null)
            return;
        if (!inputIsSelected(editorInput))
            showInput(input);
        else
            getTreeViewer().getTree().showSelection();
    }

    private Object getInputFromEditor(IEditorInput editorInput) {
        Object input= JavaUI.getEditorInputJavaElement(editorInput);
        if (input instanceof ICompilationUnit) {
            ICompilationUnit cu= (ICompilationUnit) input;
            if (!cu.getJavaProject().isOnClasspath(cu)) { // test needed for Java files in non-source folders (bug 207839)
                input= cu.getResource();
            }
        }
        if (input == null) {
            input= editorInput.getAdapter(IFile.class);
        }
        if (input == null && editorInput instanceof IStorageEditorInput) {
            try {
                input= ((IStorageEditorInput) editorInput).getStorage();
            } catch (CoreException e) {
                // ignore
            }
        }
        return input;
    }


    private boolean inputIsSelected(IEditorInput input) {
        IStructuredSelection selection= (IStructuredSelection)fViewer.getSelection();
        if (selection.size() != 1)
            return false;

        IEditorInput selectionAsInput= EditorUtility.getEditorInput(selection.getFirstElement());
        return input.equals(selectionAsInput);
    }

    boolean showInput(Object input) {
        Object element= null;

        if (input instanceof IFile && isOnClassPath((IFile)input)) {
            element= JavaCore.create((IFile)input);
        }

        if (element == null) // try a non Java resource
            element= input;

        if (element != null) {
            ISelection newSelection= new StructuredSelection(element);
            if (fViewer.getSelection().equals(newSelection)) {
                fViewer.reveal(element);
            } else {
                fViewer.setSelection(newSelection, true);

                while (element != null && fViewer.getSelection().isEmpty()) {
                    // Try to select parent in case element is filtered
                    element= getParent(element);
                    if (element != null) {
                        newSelection= new StructuredSelection(element);
                        fViewer.setSelection(newSelection, true);
                    }
                }
            }
            return true;
        }
        return false;
    }

    private boolean isOnClassPath(IFile file) {
        IJavaProject jproject= JavaCore.create(file.getProject());
        return jproject.isOnClasspath(file);
    }

    /**
     * Returns the element's parent.
     * @param element the element
     *
     * @return the parent or <code>null</code> if there's no parent
     */
    private Object getParent(Object element) {
        if (element instanceof IJavaElement)
            return ((IJavaElement)element).getParent();
        else if (element instanceof IResource)
            return ((IResource)element).getParent();
        else if (element instanceof IJarEntryResource) {
            return ((IJarEntryResource)element).getParent();
        }
        return null;
    }

    /**
     * A compilation unit or class was expanded, expand
     * the main type.
     * @param element the element
     */
    void expandMainType(Object element) {
        try {
            IType type= null;
            if (element instanceof ICompilationUnit) {
                ICompilationUnit cu= (ICompilationUnit)element;
                IType[] types= cu.getTypes();
                if (types.length > 0)
                    type= types[0];
            }
            else if (element instanceof IClassFile) {
                IClassFile cf= (IClassFile)element;
                type= cf.getType();
            }
            if (type != null) {
                final IType type2= type;
                Control ctrl= fViewer.getControl();
                if (ctrl != null && !ctrl.isDisposed()) {
                    ctrl.getDisplay().asyncExec(new Runnable() {
                        public void run() {
                            Control ctrl2= fViewer.getControl();
                            if (ctrl2 != null && !ctrl2.isDisposed())
                                fViewer.expandToLevel(type2, 1);
                        }
                    });
                }
            }
        } catch(JavaModelException e) {
            // no reveal
        }
    }

    /**
      * Returns the TreeViewer.
     * @return the tree viewer
      */
    public TreeViewer getTreeViewer() {
        return fViewer;
    }

    boolean isExpandable(Object element) {
        if (fViewer == null)
            return false;
        return fViewer.isExpandable(element);
    }

    void setWorkingSetLabel(String workingSetName) {
        fWorkingSetLabel= workingSetName;
        setTitleToolTip(getTitleToolTip());
    }

    void updateToolbar() {
        IActionBars actionBars= getViewSite().getActionBars();
        fActionSet.updateToolBar(actionBars.getToolBarManager());
    }

    /**
     * Updates the title text and title tool tip.
     * Called whenever the input of the viewer changes.
     */
    void updateTitle() {
        Object input= fViewer.getInput();
        if (input == null
            || (input instanceof IJavaModel)) {
            setContentDescription(""); //$NON-NLS-1$
            setTitleToolTip(""); //$NON-NLS-1$
        } else {
            String inputText= JavaElementLabels.getTextLabel(input, AppearanceAwareLabelProvider.DEFAULT_TEXTFLAGS);
            setContentDescription(inputText);
            setTitleToolTip(getToolTipText(input));
        }
    }

    /**
     * Sets the decorator for the package explorer.
     *
     * @param decorator a label decorator or <code>null</code> for no decorations.
     * @deprecated To be removed
     */
    @Deprecated
    public void setLabelDecorator(ILabelDecorator decorator) {
    }

    /*
     * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
     */
    public void propertyChange(PropertyChangeEvent event) {
        if (fViewer == null)
            return;

        boolean refreshViewer= false;

        if (PreferenceConstants.SHOW_CU_CHILDREN.equals(event.getProperty())) {
            boolean showCUChildren= PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.SHOW_CU_CHILDREN);
            ((StandardJavaElementContentProvider)fViewer.getContentProvider()).setProvideMembers(showCUChildren);

            refreshViewer= true;
        } else if (MembersOrderPreferenceCache.isMemberOrderProperty(event.getProperty())) {
            refreshViewer= true;
        }

        if (refreshViewer)
            fViewer.refresh();
    }

    /* (non-Javadoc)
     * @see IViewPartInputProvider#getViewPartInput()
     */
    public Object getViewPartInput() {
        if (fViewer != null) {
            return fViewer.getInput();
        }
        return null;
    }

    public void collapseAll() {
        try {
            fViewer.getControl().setRedraw(false);
            fViewer.collapseToLevel(getViewPartInput(), AbstractTreeViewer.ALL_LEVELS);
        } finally {
            fViewer.getControl().setRedraw(true);
        }
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.part.IShowInTarget#show(org.eclipse.ui.part.ShowInContext)
     */
    public boolean show(ShowInContext context) {
        ISelection selection= context.getSelection();
        if (selection instanceof IStructuredSelection) {
            // fix for 64634 Navigate/Show in/Package Explorer doesn't work
            IStructuredSelection structuredSelection= ((IStructuredSelection) selection);
            if (structuredSelection.size() == 1) {
                int res= tryToReveal(structuredSelection.getFirstElement());
                if (res == IStatus.OK)
                    return true;
                if (res == IStatus.CANCEL)
                    return false;
            } else if (structuredSelection.size() > 1) {
                selectReveal(structuredSelection);
                return true;
            }
        }

        Object input= context.getInput();
        if (input instanceof IEditorInput) {
            Object elementOfInput= getInputFromEditor((IEditorInput) input);
            return elementOfInput != null && (tryToReveal(elementOfInput) == IStatus.OK);
        }

        return false;
    }

    /**
     * Returns the <code>IShowInSource</code> for this view.
     * @return the <code>IShowInSource</code>
     */
    protected IShowInSource getShowInSource() {
        return new IShowInSource() {
            public ShowInContext getShowInContext() {
                return new ShowInContext(
                    getTreeViewer().getInput(),
                    getTreeViewer().getSelection());
            }
        };
    }

    /* (non-Javadoc)
     * @see org.eclipse.jdt.ui.IPackagesViewPart#setLinkingEnabled(boolean)
     */
    public void setLinkingEnabled(boolean enabled) {
        fLinkingEnabled= enabled;
        saveDialogSettings();

        IWorkbenchPage page= getSite().getPage();
        if (enabled) {
            page.addPartListener(fLinkWithEditorListener);

            IEditorPart editor = page.getActiveEditor();
            if (editor != null)
                editorActivated(editor);
        } else {
            page.removePartListener(fLinkWithEditorListener);
        }
        fOpenAndLinkWithEditorHelper.setLinkWithEditor(enabled);
    }

    /**
     * Returns the name for the given element. Used as the name for the current frame.
     *
     * @param element the element
     * @return the name of the frame
     */
    String getFrameName(Object element) {
        if (element instanceof IJavaElement) {
            return ((IJavaElement) element).getElementName();
        } else if (element instanceof WorkingSetModel) {
            return ""; //$NON-NLS-1$
        } else {
            return fLabelProvider.getText(element);
        }
    }

    public int tryToReveal(Object element) {
        if (revealElementOrParent(element))
            return IStatus.OK;

        WorkingSetFilterActionGroup workingSetGroup= fActionSet.getWorkingSetActionGroup().getFilterGroup();
        if (workingSetGroup != null) {
            IWorkingSet workingSet= workingSetGroup.getWorkingSet();
            if (workingSetGroup.isFiltered(getVisibleParent(element), element)) {
                String message;
                if (element instanceof IJavaElement) {
                    String elementLabel= JavaElementLabels.getElementLabel((IJavaElement)element, JavaElementLabels.ALL_DEFAULT);
                    message= Messages.format(PackagesMessages.PackageExplorerPart_notFoundSepcific, new String[] {elementLabel, BasicElementLabels.getWorkingSetLabel(workingSet)});
                } else {
                    message= Messages.format(PackagesMessages.PackageExplorer_notFound, BasicElementLabels.getWorkingSetLabel(workingSet));
                }
                if (MessageDialog.openQuestion(getSite().getShell(), PackagesMessages.PackageExplorer_filteredDialog_title, message)) {
                    workingSetGroup.setWorkingSet(null, true);
                    if (revealElementOrParent(element))
                        return IStatus.OK;
                } else {
                    return IStatus.CANCEL;
                }
            }
        }
        // try to remove filters
        CustomFiltersActionGroup filterGroup= fActionSet.getCustomFilterActionGroup();
        String[] currentFilters= filterGroup.internalGetEnabledFilterIds();
        String[] newFilters= filterGroup.removeFiltersFor(getVisibleParent(element), element, getTreeViewer().getContentProvider());
        if (currentFilters.length > newFilters.length) {
            String message;
            if (element instanceof IJavaElement) {
                String elementLabel= JavaElementLabels.getElementLabel((IJavaElement)element, JavaElementLabels.ALL_DEFAULT);
                message= Messages.format(PackagesMessages.PackageExplorerPart_removeFiltersSpecific, elementLabel);
            } else {
                message= PackagesMessages.PackageExplorer_removeFilters;
            }
            if (MessageDialog.openQuestion(getSite().getShell(), PackagesMessages.PackageExplorer_filteredDialog_title, message)) {
                filterGroup.setFilters(newFilters);
                if (revealElementOrParent(element))
                    return IStatus.OK;
            } else {
                return IStatus.CANCEL;
            }
        }
        FrameAction action= fActionSet.getUpAction();
        while (action.getFrameList().getCurrentIndex() > 0) {
            // only try to go up if there is a parent frame
            // fix for bug# 63769 Endless loop after Show in Package Explorer
            if (action.getFrameList().getSource().getFrame(IFrameSource.PARENT_FRAME, 0) == null)
                break;
            action.run();
            if (revealElementOrParent(element))
                return IStatus.OK;
        }
        return IStatus.ERROR;
    }

    private boolean revealElementOrParent(Object element) {
        if (revealAndVerify(element))
            return true;
        element= getVisibleParent(element);
        if (element != null) {
            if (revealAndVerify(element))
                return true;
            if (element instanceof IJavaElement) {
                IResource resource= ((IJavaElement)element).getResource();
                if (resource != null) {
                    if (revealAndVerify(resource))
                        return true;
                }
            }
        }
        return false;
    }

    private Object getVisibleParent(Object object) {
        // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=19104
        if (object == null)
            return null;
        if (!(object instanceof IJavaElement))
            return object;
        IJavaElement element2= (IJavaElement) object;
        switch (element2.getElementType()) {
            case IJavaElement.IMPORT_DECLARATION:
            case IJavaElement.PACKAGE_DECLARATION:
            case IJavaElement.IMPORT_CONTAINER:
            case IJavaElement.TYPE:
            case IJavaElement.METHOD:
            case IJavaElement.FIELD:
            case IJavaElement.INITIALIZER:
                // select parent cu/classfile
                element2= (IJavaElement)element2.getOpenable();
                break;
            case IJavaElement.JAVA_MODEL:
                element2= null;
                break;
        }
        return element2;
    }

    private boolean revealAndVerify(Object element) {
        if (element == null)
            return false;
        selectReveal(new StructuredSelection(element));
        return ! getSite().getSelectionProvider().getSelection().isEmpty();
    }

    public void rootModeChanged(int newMode) {
        fRootMode= newMode;
        saveDialogSettings();

        if (getRootMode() == WORKING_SETS_AS_ROOTS && fWorkingSetModel == null) {
            createWorkingSetModel();
            if (fActionSet != null) {
                fActionSet.getWorkingSetActionGroup().setWorkingSetModel(fWorkingSetModel);
            }
        }
        IStructuredSelection selection= new StructuredSelection(((IStructuredSelection) fViewer.getSelection()).toArray());
        Object input= fViewer.getInput();
        boolean isRootInputChange= JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()).equals(input)
            || (fWorkingSetModel != null && fWorkingSetModel.equals(input))
            || input instanceof IWorkingSet;
        try {
            fViewer.getControl().setRedraw(false);
            if (isRootInputChange) {
                fViewer.setInput(null);
            }
            setProviders();
            setComparator();
            fActionSet.getWorkingSetActionGroup().fillFilters(fViewer);
            if (isRootInputChange) {
                fViewer.setInput(findInputElement());
            }
            fViewer.setSelection(selection, true);
        } finally {
            fViewer.getControl().setRedraw(true);
        }
        if (isRootInputChange && getRootMode() == WORKING_SETS_AS_ROOTS && fWorkingSetModel.needsConfiguration()) {
            ConfigureWorkingSetAction action= new ConfigureWorkingSetAction(getSite());
            action.setWorkingSetModel(fWorkingSetModel);
            action.run();
            fWorkingSetModel.configured();
        }
        setTitleToolTip(getTitleToolTip());
    }

    private void createWorkingSetModel() {
        SafeRunner.run(new ISafeRunnable() {
            public void run() throws Exception {
                fWorkingSetModel= new WorkingSetModel(fMemento);
            }
            public void handleException(Throwable exception) {
                fWorkingSetModel= new WorkingSetModel(null);
            }
        });
    }


    /**
     * @return the selected working set to filter if in root mode {@link #PROJECTS_AS_ROOTS}
     */
    public IWorkingSet getFilterWorkingSet() {
        if (getRootMode() != PROJECTS_AS_ROOTS)
            return null;

        if (fActionSet == null)
            return null;

        return fActionSet.getWorkingSetActionGroup().getFilterGroup().getWorkingSet();
    }

    public WorkingSetModel getWorkingSetModel() {
        return fWorkingSetModel;
    }

    /**
     * Returns the root mode: Either {@link #PROJECTS_AS_ROOTS} or {@link #WORKING_SETS_AS_ROOTS}.
     * @return returns the root mode
     */
    public int getRootMode() {
        return fRootMode;
    }

    private void setComparator() {
        if (getRootMode() == WORKING_SETS_AS_ROOTS) {
            fViewer.setComparator(new WorkingSetAwareJavaElementSorter());
        } else {
            fViewer.setComparator(new JavaElementComparator());
        }
    }
    
    public PackageExplorerActionGroup getPackageExplorerActionGroup() {
        return fActionSet;
    }
    
}